package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.utils.*;
import com.tianji.learning.domain.dto.LearningPlanDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.service.ILearningRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 *
 * @author LYQ
 * @since 2024-06-21
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {
    private final CourseClient courseClient;
    private final CatalogueClient catalogueClient;
    //    private final ILearningRecordService learningRecordService;
    private final LearningRecordMapper learningRecordMapper;

    /**
     * 1、添加课程信息到用户课程表
     *
     * @param userId
     * @param courseIds
     */
    @Override
    @Transactional
    public void addUserLessons(Long userId, List<Long> courseIds) {
        // 1.发起远程调用，查询课程信息
        List<CourseSimpleInfoDTO> courseInfoList = courseClient.getSimpleInfoList(courseIds);
        if (ObjectUtil.isEmpty(courseInfoList)) {
            // 课程不存在，无法添加
            log.error("课程信息不存在，无法添加到课表");
            return;
        }
        //2、判定通过，执行添加课程到课表操作（批量新增）
        //2.1、新建集合，用于存储要新增的课程信息
        List<LearningLesson> lessons = new ArrayList<>(courseIds.size());
        //2.2、遍历课程信息，将课程信息封装成LearningLesson对象，添加到集合中
        for (CourseSimpleInfoDTO courseInfo : courseInfoList) {
            LearningLesson lesson = new LearningLesson();
            //2.2.1封装UserId、CourseId
            lesson.setUserId(userId);
            lesson.setCourseId(courseInfo.getId());
            Integer validDuration = courseInfo.getValidDuration();
            //2.2.2判定过期时间并处理后填充
            if (ObjectUtil.isEmpty(validDuration) || validDuration <= 0) {
                continue;
            }
            if (validDuration < 9999) {
                lesson.setExpireTime(LocalDateTime.now().plusMonths(courseInfo.getValidDuration()));
            }
            lessons.add(lesson);
        }
        //3、批量新增
        this.saveBatch(lessons);
    }


    /**
     * 2、分页查询用户个人课程表
     *
     * @param query
     */
    @Override
    public PageDTO<LearningLessonVO> queryMyLessonPage(PageQuery query) {
        // 1.获取当前登录用户
        Long userId = UserContext.getUser();
        // 2.分页查询
        Page<LearningLesson> page = lambdaQuery()
                .eq(LearningLesson::getUserId, userId) // where user_id = #{userId}
                .page(query.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = page.getRecords();
        if (CollUtils.isEmpty(records)) {
            return PageDTO.empty(page);
        }
        // 3.查询课程信息
        Map<Long, CourseSimpleInfoDTO> cMap = queryCourseSimpleInfoList(records);

        // 4.封装VO返回
        List<LearningLessonVO> list = new ArrayList<>(records.size());
        // 4.1.循环遍历，把LearningLesson转为VO
        for (LearningLesson r : records) {
            // 4.2.拷贝基础属性到vo
            LearningLessonVO vo = BeanUtils.copyBean(r, LearningLessonVO.class);
            // 4.3.获取课程信息，填充到vo
            CourseSimpleInfoDTO cInfo = cMap.get(r.getCourseId());
            vo.setCourseName(cInfo.getName());
            vo.setCourseCoverUrl(cInfo.getCoverUrl());
            vo.setSections(cInfo.getSectionNum());
            list.add(vo);
        }
        return PageDTO.of(page, list);
    }

    /**
     * 3、查询正在学习的课程
     */
    @Override
    public LearningLessonVO queryMyCurrentLesson() {
        //1、查询当前的用户Id
        Long userId = UserContext.getUser();
        // 2.查询正在学习的课程 select * from xx where user_id = #{userId} AND status = 1 order by latest_learn_time limit 1
        LearningLesson lesson = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getStatus, LessonStatus.LEARNING.getValue())
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1")
                .one();
        if (lesson == null) {
            return null;
        }
        // 3.拷贝PO基础属性到VO
        LearningLessonVO vo = BeanUtils.copyBean(lesson, LearningLessonVO.class);
        // 4.查询课程信息
        CourseFullInfoDTO cInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
        if (cInfo == null) {
            throw new BadRequestException("课程不存在");
        }
        vo.setCourseName(cInfo.getName());
        vo.setCourseCoverUrl(cInfo.getCoverUrl());
        vo.setSections(cInfo.getSectionNum());
        // 5.统计课表中的课程数量 select count(1) from xxx where user_id = #{userId}
        Integer courseAmount = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .count();
        vo.setCourseAmount(courseAmount);
        // 6.查询小节信息
        List<CataSimpleInfoDTO> cataInfos =
                catalogueClient.batchQueryCatalogue(CollUtils.singletonList(lesson.getLatestSectionId()));
        if (!CollUtils.isEmpty(cataInfos)) {
            CataSimpleInfoDTO cataInfo = cataInfos.get(0);
            vo.setLatestSectionName(cataInfo.getName());
            vo.setLatestSectionIndex(cataInfo.getCIndex());
        }
        return vo;
    }


    /**
     * 将用户所学习课程数据，根据id远程查询课程实体并转为id-实体的map集合
     *
     * @param records
     * @return
     */
    private Map<Long, CourseSimpleInfoDTO> queryCourseSimpleInfoList(List<LearningLesson> records) {
        // 3.1.获取课程id
        Set<Long> cIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
        // 3.2.查询课程信息
        List<CourseSimpleInfoDTO> cInfoList = courseClient.getSimpleInfoList(cIds);
        if (CollUtils.isEmpty(cInfoList)) {
            // 课程不存在，无法添加
            throw new BadRequestException("课程信息不存在！");
        }
        // 3.3.把课程集合处理成Map，key是courseId，值是course本身
        Map<Long, CourseSimpleInfoDTO> cMap = cInfoList.stream()
                .collect(Collectors.toMap(CourseSimpleInfoDTO::getId, Function.identity()));
        return cMap;
    }


    /**
     * 创建用户的学习计划
     *
     * @param learningPlanDTO
     */
    @Override
    public void createMyLessonsPlan(LearningPlanDTO learningPlanDTO) {
//        LearningLesson lesson = new LearningLesson();
//        lesson.setPlanStatus(PlanStatus.PLAN_RUNNING);
//        lesson.setWeekFreq(weekFreq);
        //1、搜索课表信息
        LearningLesson learningLesson = this.getOne(buildUserIdAndCourseIdWrapper(UserContext.getUser(), learningPlanDTO.getCourseId()));
        AssertUtils.isNotNull(learningLesson, "课程信息不存在！");
        //2、更新课表信息
        this.lambdaUpdate()
                .eq(LearningLesson::getCourseId, learningPlanDTO.getCourseId())
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .set(learningLesson.getPlanStatus() == PlanStatus.NO_PLAN, LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .set(LearningLesson::getWeekFreq, learningPlanDTO.getFreq())
                .update();
    }

    @Override
    public LearningPlanPageVO queryMyLessonsPlan(PageQuery query) {
        //初始化LearningPlanPageVo
        LearningPlanPageVO learningPlanPageVO = new LearningPlanPageVO();
        //初始化Vo集合
        List<LearningPlanVO> list;
        //1、分页查询用户课表信息
        Page<LearningLesson> page = this.lambdaQuery()
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .in(LearningLesson::getStatus, LessonStatus.LEARNING, LessonStatus.NOT_BEGIN)
                .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .page(query.toMpPage("latest_learn_time", false));
        List<LearningLesson> learningLessons = page.getRecords();
        //1.1如果用户课表为空，代表用户没有添加课程计划直接返回null
        if (ObjectUtil.isEmpty(learningLessons)) {
            return learningPlanPageVO.pageInfo(PageDTO.empty(page));
        }
        //2、查询课程信息
        Map<Long, CourseSimpleInfoDTO> courseMap = queryCourseSimpleInfoList(learningLessons);
        //3、查询学习记录
        //3.1、获取本周始末时间对象
        LocalDateTime beginTime = DateUtils.getWeekBeginTime(LocalDate.now());
        LocalDateTime endTime = DateUtils.getWeekEndTime(LocalDate.now());
        //3.2、查询本周已学习完成的学习记录
//        List<LearningRecord> recordList = learningRecordService.lambdaQuery()
//                .eq(LearningRecord::getUserId, UserContext.getUser())
//                .eq(LearningRecord::getFinished, true)
//                .between(LearningRecord::getFinishTime, beginTime, endTime)
//                .groupBy(LearningRecord::getLessonId)
//                .list();
        Set<Long> lessonIds = learningLessons.stream().map(LearningLesson::getId).collect(Collectors.toSet());
        List<LearningRecord> recordList = learningRecordMapper.selectList(new QueryWrapper<LearningRecord>().lambda()
                .eq(LearningRecord::getUserId, UserContext.getUser())
                .eq(LearningRecord::getFinished, true)
                .in(LearningRecord::getLessonId, lessonIds)
                .between(LearningRecord::getFinishTime, beginTime, endTime)
        );
        //3.3、Stream流遍历，将学习记录按课表id分组，并统计出每门课程已学习章节数
        Map<Long, Long> recordMap = recordList.stream().collect(Collectors.groupingBy(LearningRecord::getLessonId, Collectors.counting()));
        //4、填充LearningPlanVoList数据
        list = learningLessons.stream().map(learningLesson -> {
            LearningPlanVO learningPlanVO = new LearningPlanVO();
            learningPlanVO.setId(learningLesson.getId());
            learningPlanVO.setCourseId(learningLesson.getCourseId());
            learningPlanVO.setLearnedSections(learningLesson.getLearnedSections());
            learningPlanVO.setWeekFreq(learningLesson.getWeekFreq());
            learningPlanVO.setLatestLearnTime(learningLesson.getLatestLearnTime());
            if (ObjectUtil.isNotEmpty(courseMap)) {
                learningPlanVO.setCourseName(courseMap.get(learningLesson.getCourseId()).getName());
                learningPlanVO.setSections(courseMap.get(learningLesson.getCourseId()).getSectionNum());
            }
            learningPlanVO.setWeekLearnedSections(recordMap.getOrDefault(learningLesson.getId(), 0L).intValue());
            return learningPlanVO;
        }).collect(Collectors.toList());
        //5、处理LearningPlanPageVo
        //5.1填充周完成数据
        Integer weekFinishedCount = learningRecordMapper.selectCount(new LambdaQueryWrapper<LearningRecord>()
                .eq(LearningRecord::getUserId, UserContext.getUser())
                .eq(LearningRecord::getFinished, true)
                .in(LearningRecord::getLessonId, lessonIds)
                .between(LearningRecord::getFinishTime, beginTime, endTime)
        );
        learningPlanPageVO.setWeekFinished(weekFinishedCount);
        //5.2填充周计划数据
        LearningLesson weekTotalPlan = this.baseMapper.selectOne(
                new QueryWrapper<LearningLesson>().select("sum(week_freq) as week_freq")
                        .eq("user_id", UserContext.getUser())
                        .eq("plan_status", PlanStatus.PLAN_RUNNING)
                        .in("status", LessonStatus.LEARNING, LessonStatus.NOT_BEGIN));
        learningPlanPageVO.setWeekTotalPlan(weekTotalPlan.getWeekFreq());
        //5.3填充本周积分
        learningPlanPageVO.setWeekPoints(learningPlanPageVO.getWeekFinished() * 5);
        return learningPlanPageVO.pageInfo(page.getTotal(), page.getPages(), list);
    }


    /**
     * 删除用户课表中的课程
     *
     * @param userId
     * @param courseId
     * @return LearningLesson
     */
    @Override
    public LearningLesson queryLessonByUserIdAndCourseId(Long userId, Long courseId) {
        return this.getOne(buildUserIdAndCourseIdWrapper(userId, courseId));
    }

    /**
     * 根据userId和courseId删除对应课表课程
     *
     * @param userId
     * @param courseId
     */
    @Override
    public void deleteMyLessonCourse(Long userId, Long courseId) {
        if (userId == null) {
            userId = UserContext.getUser();
        }
        this.remove(buildUserIdAndCourseIdWrapper(userId, courseId));
    }

    /**
     * 校验当前用户课表中是否有该课程
     * 有则返回课程ID，无则返回null
     *
     * @param courseId
     * @return
     */
    @Override
    public Long isLessonValid(Long courseId) {
        // 1.获取登录用户
        Long userId = UserContext.getUser();
        if (userId == null) {
            return null;
        }
        //2、查询当前用户中对应courseId的课程
        LearningLesson learningLesson = this.getOne(buildUserIdAndCourseIdWrapper(UserContext.getUser(), courseId));
        if (learningLesson != null) {
            //2.1、存在则返回课表Id
            return learningLesson.getId();
        }
        return null;
    }

    /**
     * 查询指定课程信息
     *
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonVO queryLessonByCourseId(Long courseId) {
        // 1.获取当前登录用户
        Long userId = UserContext.getUser();
        // 2.查询课程信
        LearningLesson learningLesson = getOne(buildUserIdAndCourseIdWrapper(userId, courseId));
        if (learningLesson == null) {
            return null;
        }
        // 3.处理VO
        return BeanUtils.copyBean(learningLesson, LearningLessonVO.class);
    }


    /**
     * 查询指定课程学习人数
     *
     * @param courseId
     * @return
     */
    @Override
    public Integer countLearningLessonByCourse(Long courseId) {
        return this.lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .in(LearningLesson::getStatus,
                        LessonStatus.NOT_BEGIN.getValue(),
                        LessonStatus.LEARNING.getValue(),
                        LessonStatus.FINISHED.getValue())
                .count();
    }


    /**
     * 构建根据userId和courseId查询对应课表的课程的查询条件
     *
     * @param userId
     * @param courseId
     * @return
     */
    private LambdaQueryWrapper<LearningLesson> buildUserIdAndCourseIdWrapper(Long userId, Long courseId) {
        LambdaQueryWrapper<LearningLesson> queryWrapper = new QueryWrapper<LearningLesson>()
                .lambda()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId);
        return queryWrapper;
    }

    /**
     * 对所有用户的课表过期与否进行状态检查并更新
     */
    @Override
    public void updateLearningLessonStatus() {
        this.lambdaUpdate()
                .in(LearningLesson::getStatus, LessonStatus.NOT_BEGIN, LessonStatus.LEARNING, LessonStatus.FINISHED)
                .lt(LearningLesson::getExpireTime, LocalDateTime.now())
                .set(LearningLesson::getStatus, LessonStatus.EXPIRED)
                .update();
    }
}
